package com.demo.wy;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT高级功能演示类
 * 演示了JWT在实际应用中的高级用法，包括：
 * 1. 访问令牌和刷新令牌的使用
 * 2. Web应用中的JWT认证流程
 * 3. 不同的JWT签名算法
 */
public class JWTAdvancedDemo {
    // 用于访问令牌的签名密钥
    private static final String SECRET = "your-256-bit-secret";
    // 用于刷新令牌的签名密钥（使用不同的密钥增加安全性）
    private static final String REFRESH_SECRET = "your-refresh-token-secret";
    // 访问令牌的有效期：1分钟
    private static final long ACCESS_TOKEN_EXPIRE = 1000 * 60;
    // 刷新令牌的有效期：24小时
    private static final long REFRESH_TOKEN_EXPIRE = 1000 * 60 * 60 * 24;

    public static void main(String[] args) {
        // 1. 演示访问令牌和刷新令牌的使用
        demoRefreshToken();

        // 2. 演示在Web应用中的JWT认证流程
        demoWebAuthentication();

        // 3. 演示不同的JWT签名算法
        demoSigningAlgorithms();
    }

    /**
     * 演示刷新令牌的使用流程
     * 包括：
     * 1. 生成初始的访问令牌和刷新令牌
     * 2. 模拟访问令牌过期
     * 3. 使用刷新令牌获取新的访问令牌
     */
    private static void demoRefreshToken() {
        System.out.println("=== 刷新令牌演示 ===");
        try {
            // 生成访问令牌和刷新令牌
            Map<String, String> tokens = generateTokenPair("user123");
            System.out.println("初始访问令牌: " + tokens.get("accessToken"));
            System.out.println("刷新令牌: " + tokens.get("refreshToken"));

            // 模拟等待访问令牌过期
            Thread.sleep(2000);

            // 使用刷新令牌获取新的访问令牌
            String newAccessToken = refreshAccessToken(tokens.get("refreshToken"));
            System.out.println("新的访问令牌: " + newAccessToken);
        } catch (Exception e) {
            System.out.println("刷新令牌操作异常: " + e.getMessage());
        }
    }

    /**
     * 演示Web应用中的JWT认证流程
     * 包括：
     * 1. 用户登录验证
     * 2. 生成JWT令牌
     * 3. 模拟API请求验证
     */
    private static void demoWebAuthentication() {
        System.out.println("\n=== Web认证流程演示 ===");
        try {
            // 模拟用户登录
            String userId = "user123";
            String password = "password123";
            
            // 1. 验证用户凭据（模拟）
            if (simulateUserAuthentication(userId, password)) {
                // 2. 生成JWT令牌
                Map<String, String> tokens = generateTokenPair(userId);
                System.out.println("登录成功，生成的访问令牌: " + tokens.get("accessToken"));

                // 3. 模拟API请求验证
                simulateApiRequest(tokens.get("accessToken"));
            }
        } catch (Exception e) {
            System.out.println("认证流程异常: " + e.getMessage());
        }
    }

    /**
     * 演示不同的JWT签名算法
     * 包括：
     * 1. HMAC SHA256 算法
     * 2. HMAC SHA512 算法（更安全但性能略低）
     */
    private static void demoSigningAlgorithms() {
        System.out.println("\n=== JWT签名算法演示 ===");
        try {
            // 1. HMAC SHA256
            String hmacToken = JWT.create()
                    .withClaim("userId", "123")
                    .sign(Algorithm.HMAC256(SECRET));
            System.out.println("HMAC256签名令牌: " + hmacToken);

            // 2. HMAC SHA512
            String hmac512Token = JWT.create()
                    .withClaim("userId", "123")
                    .sign(Algorithm.HMAC512(SECRET));
            System.out.println("HMAC512签名令牌: " + hmac512Token);

        } catch (Exception e) {
            System.out.println("签名算法演示异常: " + e.getMessage());
        }
    }

    /**
     * 生成访问令牌和刷新令牌对
     * @param userId 用户ID
     * @return 包含访问令牌和刷新令牌的Map
     */
    private static Map<String, String> generateTokenPair(String userId) {
        Map<String, String> tokens = new HashMap<>();

        // 生成访问令牌
        String accessToken = JWT.create()
                .withClaim("userId", userId)
                .withClaim("tokenType", "access")
                .withExpiresAt(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRE))
                .sign(Algorithm.HMAC256(SECRET));

        // 生成刷新令牌
        String refreshToken = JWT.create()
                .withClaim("userId", userId)
                .withClaim("tokenType", "refresh")
                .withExpiresAt(new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRE))
                .sign(Algorithm.HMAC256(REFRESH_SECRET));

        tokens.put("accessToken", accessToken);
        tokens.put("refreshToken", refreshToken);
        return tokens;
    }

    /**
     * 使用刷新令牌获取新的访问令牌
     * @param refreshToken 刷新令牌
     * @return 新的访问令牌
     * @throws RuntimeException 当刷新令牌无效或过期时
     */
    private static String refreshAccessToken(String refreshToken) {
        try {
            // 验证刷新令牌
            DecodedJWT jwt = JWT.require(Algorithm.HMAC256(REFRESH_SECRET))
                    .build()
                    .verify(refreshToken);

            // 确保是刷新令牌
            if (!"refresh".equals(jwt.getClaim("tokenType").asString())) {
                throw new RuntimeException("无效的刷新令牌");
            }

            // 生成新的访问令牌
            return JWT.create()
                    .withClaim("userId", jwt.getClaim("userId").asString())
                    .withClaim("tokenType", "access")
                    .withExpiresAt(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRE))
                    .sign(Algorithm.HMAC256(SECRET));
        } catch (TokenExpiredException e) {
            throw new RuntimeException("刷新令牌已过期");
        } catch (Exception e) {
            throw new RuntimeException("刷新访问令牌失败");
        }
    }

    /**
     * 模拟用户认证过程
     * @param userId 用户ID
     * @param password 密码
     * @return 认证是否成功
     */
    private static boolean simulateUserAuthentication(String userId, String password) {
        // 模拟用户认证过程
        return "user123".equals(userId) && "password123".equals(password);
    }

    /**
     * 模拟API请求验证过程
     * @param accessToken 访问令牌
     */
    private static void simulateApiRequest(String accessToken) {
        try {
            // 验证访问令牌
            DecodedJWT jwt = JWT.require(Algorithm.HMAC256(SECRET))
                    .build()
                    .verify(accessToken);

            // 确保是访问令牌
            if (!"access".equals(jwt.getClaim("tokenType").asString())) {
                throw new RuntimeException("无效的访问令牌类型");
            }

            String userId = jwt.getClaim("userId").asString();
            System.out.println("API请求验证成功，用户ID: " + userId);
        } catch (TokenExpiredException e) {
            System.out.println("访问令牌已过期，请使用刷新令牌获取新的访问令牌");
        } catch (Exception e) {
            System.out.println("API请求验证失败: " + e.getMessage());
        }
    }
}